<template>
  <div class="box">
    <div class="slide">
      <ul>
        <transition-group name="flip-list">
          <li
            v-for="(item, index) in groups"
            draggable="true"
            :key="item.id"
            :class="[
              'custom',
              hoverGroupRow.id === item.id ? 'custom-selected' : '',
              newNum && newNum === item.id ? dragClass : '',
            ]"
            @dblclick.stop="onEditGroupInp(item, index)"
            @click="selectGroupRow(item, index)"
            @dragstart="dragGroupStart(item, index)"
            @dragenter="dragGroupEnter(item, index)"
            @dragend="dragGroupEnd(item, index)"
            @contextmenu="onContextMenu($event, groups, index, 'group')"
          >
            <div
              class="group-title"
              v-if="editGroupRow.id !== item.id"
              :title="item.value"
            >
              <p>{{ item.value }}</p>
              <span class="num">{{ item?.children.length }}</span>
            </div>
            <el-input
              v-else
              ref="editGroupInp"
              v-model="item.value"
              placeholder="Please input"
              autofocus
              size="mini"
              clearable
              @keyup.enter="onBlurEditGroup($event, index, 'keyup')"
              @blur="onBlurEditGroup($event, index, 'blur')"
            />
          </li>
        </transition-group>
        <li v-if="newGroupShow" class="addinp">
          <el-input
            ref="newGroupInp"
            v-model="newGroupVal"
            autofocus
            placeholder="Please input"
            size="mini"
            clearable
            @keyup.enter="onBlurNewGroup($event, 'keyup')"
            @blur="onBlurNewGroup($event, 'blur')"
          />
        </li>
      </ul>
      <div class="bottom">
        <p class="custom" @click="addGroup">添加分组</p>
        <p class="custom">设置</p>
      </div>
    </div>
    <div class="main">
      <div class="top">
        <h2 :title="hoverGroupRow.value">{{ hoverGroupRow.value }}</h2>
        <div class="right">
          <span class="control-btn" @click="addBlock">添加</span>
          <span
            class="control-btn"
            @click="onContextMenu($event, groups, hoerGroupIndex, 'group')"
            >更多</span
          >
        </div>
      </div>
      <div v-if="groups[hoerGroupIndex]" class="block-box">
        <transition-group name="flip-list">
          <div
            v-for="(item, index) in groups[hoerGroupIndex].children || []"
            :key="item.id"
            :data-id="item.id"
            :ref="setItemRef"
            class="block-list"
            draggable="true"
            @dragstart="dragBlockStart($event, item, index)"
            @dragend="dragBlockEnd($event, item, index)"
            @dragenter="dragBlockEnter(item, index)"
          >
            <div
              class="block-item"
              @dblclick.prevent="dbClickBlock(item, index)"
              @contextmenu="
                onContextMenu(
                  $event,
                  groups[hoerGroupIndex].children,
                  index,
                  'block'
                )
              "
            >
              <el-checkbox
                v-model="item.isFinised"
                @change="changeFinised($event, item, index)"
              ></el-checkbox>
              <el-input
                :ref="item.id"
                v-model="item.value"
                autofocus
                autosize
                type="textarea"
                :disabled="item.isEdit"
                placeholder="Please input"
                resize="none"
                class="inp"
                :style="[
                  item.isEdit ? 'user-select:none' : '',
                  item.isFinised ? 'text-decoration-line: line-through' : '',
                ]"
                @keydown="onBlurSaveBlock($event, item, index, 'keydown')"
                @blur="onBlurSaveBlock($event, item, index, 'blur')"
              />
            </div>
          </div>
        </transition-group>
      </div>
    </div>
  </div>
  <div
    v-if="cardShow"
    class="card"
    @click="cardShow = false"
    @contextmenu.prevent="clearCard"
  >
    <div
      class="card-body"
      :style="{
        top: cardTop + 'px',
        left: cardLeft + 'px',
      }"
    >
      <div class="card-li" @click.stop="editCard">编辑</div>
      <div class="card-li" @click.stop="delCard">删除</div>
    </div>
  </div>
</template>
<script lang="ts">
import { MoreFilled, Plus } from "@element-plus/icons";
import {
  getCurrentInstance,
  defineComponent,
  nextTick,
  ref,
  reactive,
  toRefs,
  onMounted,
} from "vue";
interface GroupProps {
  value: string;
  id: string;
  children?: GroupProps[];
  isFinised?: Boolean;
  isEdit?: Boolean;
}

interface DataProps {
  editGroupRow: GroupProps;
  hoverGroupRow: GroupProps;
  hoerGroupIndex: number;
  oldNum: string;
  newNum: string;
  dragClass: string;
  dragOld: number;
  blockRefs: any;
  flag: boolean;
  cardShow: boolean;
  cardTop: number;
  cardLeft: number;
  cardKey: "group" | "block" | "";
  cardData: GroupProps[];
  cardDataIndex: number;
  saveBlock: Function;
  onEditGroupInp: Function;
  clearCard: Function;
  dbClickBlock: Function;
  delGroup: Function;
  selectGroupRow: Function;
}
export default defineComponent({
  setup() {
    const groups: any = ref([]);
    const newGroupShow = ref(false);
    const newGroupVal = ref("");
    const newGroupInp: any = ref();
    const editGroupInp: any = ref();

    // 使用reactive来一次性返回所有参数和函数，并且不用带.value
    const data: DataProps = reactive({
      editGroupRow: {
        id: "",
        value: "",
      },
      hoerGroupIndex: 0,
      hoverGroupRow: {
        id: "",
        value: "",
        children: [],
      },
      oldNum: "",
      newNum: "",
      dragClass: "",
      dragOld: -1,
      blockRefs: {},

      setItemRef: (el: any, id: string) => {
        if (!el) return;
        data.blockRefs[el.dataset.id] = el;
      },
      // 双击编辑输入框
      onEditGroupInp: async (item: GroupProps, index: number) => {
        data.editGroupRow = item;
        await nextTick();
        editGroupInp.value.focus();
      },
      // 单击分组
      selectGroupRow: (item: GroupProps, index: number) => {
        data.hoverGroupRow = item;
        data.hoerGroupIndex = index;
      },
      // 添加任务
      addBlock: async () => {
        if(!data.hoverGroupRow.id) return
        const item = {
          id: getuuid(),
          value: "",
          isFinised: false,
          isEdit: false,
        };
        const list = groups.value[data.hoerGroupIndex].children;
        const lastIndex = list.findIndex(
          (i: GroupProps) => i.isFinised === true
        );
        if (lastIndex !== -1) {
          list.splice(lastIndex, 0, item);
        } else {
          list.push(item);
        }
        await nextTick();
        data.blockRefs[item.id].getElementsByTagName("textarea")[0].focus();
      },
      // 编辑任务
      dbClickBlock: async (item: GroupProps, index: number) => {
        item.isEdit = false;
        await nextTick();
        data.blockRefs[item.id].getElementsByTagName("textarea")[0].focus();
      },
      flag: false,
      // 失焦保存任务
      onBlurSaveBlock: async (
        event: any,
        item: GroupProps,
        index: number,
        key: string
      ) => {
        if (key === "blur" && data.flag) return;
        const value: string = event.target.value;
        if (key === "blur") {
          data.saveBlock(value, item, index);
        } else if (event.keyCode === 13) {
          data.flag = true;
          data.saveBlock(value, item, index);
          event.preventDefault(); // 阻止浏览器默认换行操作
          setTimeout(() => {
            data.flag = false;
          }, 1000);
          return false;
        }
      },
      saveBlock: (value: string, item: GroupProps, index: number) => {
        const list = groups.value[data.hoerGroupIndex].children;
        if (value !== "") {
          list.splice(index, 1, {
            ...item,
            value,
            isEdit: true,
          });
        } else {
          list.splice(index, 1);
        }

        saveStorage("todoGroups", groups.value);
      },
      // 更改任务完成状态
      changeFinised: (value: any, item: GroupProps, index: number) => {
        let datas = { ...item };
        // 先删除
        groups.value[data.hoerGroupIndex].children.splice(index, 1);
        if (value) {
          // 移动到已完成
          groups.value[data.hoerGroupIndex].children.push({
            ...datas,
            isFinised: value,
          });
        } else {
          // 移动至顶部
          groups.value[data.hoerGroupIndex].children.unshift({
            ...datas,
            isFinised: value,
          });
        }
        saveStorage("todoGroups", groups.value);
      },
      // 记录分组初始信息
      dragGroupStart: (item: GroupProps, index: number) => {
        data.oldNum = item.id;
        data.dragOld = index;
      },
      // 分组做最终操作
      dragGroupEnd: (item: GroupProps, index: number) => {
        if (data.oldNum != data.newNum) {
          let oldIndex = groups.value.findIndex(
            (i: GroupProps) => data.oldNum === i.id
          );
          let newIndex = groups.value.findIndex(
            (i: GroupProps) => data.newNum === i.id
          );
          let newItems = [...groups.value];
          // 删除老的节点
          newItems.splice(oldIndex, 1);
          // 在列表中目标位置增加新的节点
          newItems.splice(newIndex, 0, groups.value[oldIndex]);
          // this.items一改变，transition-group就起了作用
          groups.value = [...newItems];
          // 新节点所在位置的当前hover节点
          const hoverIndex = groups.value.findIndex(
            (i: GroupProps) => data.hoverGroupRow.id === i.id
          );
          data.hoverGroupRow = groups.value[hoverIndex];
          data.hoerGroupIndex = hoverIndex;
        }
        data.oldNum = "";
        data.newNum = "";
        data.dragOld = -1;
      },
      // 记录分组移动过程中信息
      dragGroupEnter: (item: GroupProps, index: number) => {
        data.newNum = item.id;
        if (index > data.dragOld) {
          data.dragClass = "hover-border-down";
        } else {
          data.dragClass = "hover-border-up";
        }
      },
      // 记录任务初始信息
      dragBlockStart: (event: any, item: GroupProps, index: number) => {
        event.target.style.opacity = 1;
        event.target.style.cursor = "move";
        data.oldNum = item.id;
      },
      // 任务做最终操作
      dragBlockEnd: (event: any, item: GroupProps, index: number) => {
        event.target.style.opacity = 1;
        event.target.style.cursor = "default";
        if (data.oldNum != data.newNum) {
          let block = groups.value[data.hoerGroupIndex].children;
          let oldIndex = block.findIndex(
            (i: GroupProps) => data.oldNum === i.id
          );
          let newIndex = block.findIndex(
            (i: GroupProps) => data.newNum === i.id
          );
          let newItems = [...block];
          // 删除老的节点
          newItems.splice(oldIndex, 1);
          // 在列表中目标位置增加新的节点
          newItems.splice(newIndex, 0, block[oldIndex]);
          // this.items一改变，transition-group就起了作用
          groups.value[data.hoerGroupIndex].children = [...newItems];
        }
      },
      // 记录任务移动过程中信息
      dragBlockEnter: (item: GroupProps, index: number) => {
        data.newNum = item.id;
      },
      // 删除分组
      delGroup: (item: GroupProps, index: number) => {
        groups.value.splice(index, 1);
        saveStorage("todoGroups", groups.value);
        if (index !== 0) {
          const currentIndex = index - 1;
          data.hoverGroupRow = groups.value[currentIndex];
          data.hoerGroupIndex = currentIndex;
        } else if (index === 0 && groups.value.length === 1) {
          data.hoverGroupRow = groups.value[0];
          data.hoerGroupIndex = 0;
        } else if (groups.value.length === 0) {
          data.hoverGroupRow = {
            id: "",
            value: "",
            children: [],
          };
          data.hoerGroupIndex = 0;
        }
      },
      editGroup: (item: GroupProps, index: number) => {},
      cardShow: false,
      cardTop: 0,
      cardLeft: 0,
      cardData: [],
      cardKey: "",
      cardDataIndex: -1,
      // 右键菜单
      onContextMenu: (
        event: any,
        list: GroupProps[],
        index: number,
        key: "" | "group" | "block"
      ) => {
        event.preventDefault();
        data.cardTop = event.clientY;
        data.cardLeft = event.clientX;
        if (document.body.clientWidth <= event.clientX + 100) {
          data.cardLeft = event.clientX - 80;
        }
        if (document.body.clientHeight <= event.clientY + 120) {
          data.cardTop = event.clientY - 70;
        }
        data.cardKey = key;
        data.cardData = list;
        data.cardDataIndex = index;
        data.cardShow = true;
      },
      editCard: () => {
        if (data.cardKey === "group") {
          data.onEditGroupInp(
            data.cardData[data.cardDataIndex],
            data.cardDataIndex
          );
        } else {
          data.dbClickBlock(
            data.cardData[data.cardDataIndex],
            data.cardDataIndex
          );
        }
        data.clearCard();
      },
      delCard: () => {
        if (data.cardKey === "group") {
          data.delGroup(data.cardData[data.cardDataIndex], data.cardDataIndex);
        } else {
          data.cardData.splice(data.cardDataIndex, 1);
        }
        data.clearCard();
      },
      clearCard: () => {
        data.cardKey = "";
        data.cardData = [];
        data.cardDataIndex = -1;
        data.cardShow = false;
      },
    });
    // -------------------------------------------------------------
    // 添加分组
    const addGroup = async () => {
      newGroupShow.value = true;
      newGroupVal.value = "";
      await nextTick();
      //利用定义的Ref获取dom结构
      newGroupInp.value.focus();
    };

    // 失去新增分组焦点
    const onBlurNewGroup = (event: any, type: string) => {
      // 防止回车和失焦同时触发
      if (type === "keyup") {
        data.flag = true;
        setTimeout(() => {
          data.flag = false;
        }, 1000);
      }
      if (type === "blur" && data.flag) return;
      const value = event.target.value;
      if (value === "") return (newGroupShow.value = false);
      if (groups.value.findIndex((i: any) => i.value === value) !== -1)
        return alert("名称不能重复！");
      groups.value.push({
        id: getuuid(),
        value,
        children: [],
      });
      saveStorage("todoGroups", groups.value);
      newGroupShow.value = false;
      // 如果长度只有1，那么默认选中当前分组
      if (groups.value.length < 2) {
        data.selectGroupRow(groups.value[0], 0);
      }
    };

    // 编辑分组失去焦点
    const onBlurEditGroup = (event: any, index: number, type: string) => {
      if (type === "keyup") {
        data.flag = true;
        setTimeout(() => {
          data.flag = false;
        }, 1000);
      }
      if (type === "blur" && data.flag) return;
      const value = event.target.value;
      if (value === "") {
        groups.value.splice(index, 1);
      } else if (value !== data.editGroupRow.value) {
        groups.value.splice(index, 1, { ...groups.value[index], value });
      }
      saveStorage("todoGroups", groups.value);
      newGroupShow.value = false;
      data.editGroupRow = { id: "", value: "" };
    };

    // 保存缓存数据
    const saveStorage = (key: string, value: any) => {
      window.localStorage.setItem(key, JSON.stringify(value));
    };

    const refData = toRefs(data);

    onMounted(() => {
      const { todoGroups } = window.localStorage;
      if (todoGroups) {
        groups.value = JSON.parse(todoGroups);
        if (groups.value.length) {
          data.hoverGroupRow = groups.value[0];
          data.hoerGroupIndex = 0;
        }
      }
    });
    const getuuid = () => {
      function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
      }
      return `${S4() + S4()}-${S4()}-${S4()}-${S4()}-${S4() + S4() + S4()}`;
    };
    return {
      groups,
      newGroupShow,
      newGroupVal,
      newGroupInp,
      addGroup,
      onBlurNewGroup,
      editGroupInp,
      onBlurEditGroup,
      ...refData,
    };
  },
});
</script>
<style lang="scss">
* {
  margin: 0;
  padding: 0;
  list-style: none;
  outline: 0;
  border: 0;
  box-sizing: border-box;
}
$hover-color: rgba(131, 231, 120, 0.3);

.flip-list-move {
  transition: transform 0.5s;
}
.box {
  display: flex;
  height: calc(100vh - 61px);
}

.slide {
  width: 200px;
  height: calc(100% - 40px);
  margin: 20px 10px 20px;
  background: #f7f8f9;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border-radius: 6px;
  .custom {
    padding: 8px 20px;
    // height: 32px;
    margin: 6px 8px;
    text-align: left;
    border-radius: 12px;
    cursor: pointer;
    &:hover {
      background-color: $hover-color;
    }
  }
  .group-title {
    display: flex;
    align-items: center;
    p {
      width: 116px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      
      flex: 1;
    }
    .num {
      display: inline-block;
      text-align: right;
      width: 28px;
      color: #999999;
    }
  }
  .custom-selected {
    background-color: $hover-color;
  }
  .hover-border-up {
    position: relative;
    transition: all 0.3s;
    &::before {
      content: "";
      display: block;
      position: absolute;
      left: 0;
      top: 0;
      height: 1px;
      width: 100%;
      background-color: var(--el-color-primary);
    }
  }
  .hover-border-down {
    position: relative;
    transition: all 0.3s;
    &::before {
      content: "";
      display: block;
      position: absolute;
      left: 0;
      bottom: 0;
      height: 1px;
      width: 100%;
      background-color: var(--el-color-primary);
    }
  }
  .addinp {
    margin: 12px 15px;
  }
  ul {
    p {
    }
  }
  .bottom {
    border-top: 1px solid #d1d1d1;
  }
}

.card-edit {
  padding: 0 4px;
  border-radius: 4px;
  background-color: #fff;
  li {
    padding: 8px 20px;
    cursor: pointer;
    &:hover {
      background-color: $hover-color;
    }
  }
}
.main {
  flex: 1;
  width: 100%;
  min-width: 450px;
  margin: 20px 10px 20px 5px;
  height: calc(100% - 40px);
  background: #f7f8f9;
  border-radius: 6px;
  padding: 10px 0 0;
  .top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: #f7f8f9;
    padding: 12px 10px;
    border-bottom: 1px solid #e1e1e1;
    h2 {
      width:88%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      text-align: left;
    }
    .right {
      .control-btn {
        font-size: 14px;
        margin: 5px 8px;
        cursor: pointer;
        transition: all 0.3s;
        &:hover {
          color: rgba(111, 206, 100, 0.7);
        }
      }
    }
  }
  .block-box {
    width: 100%;
    padding: 0 10px;
    height: calc(100% - 60px);
    overflow: auto;
    &::-webkit-scrollbar {
      display: none;
    }
  }
  .block-list {
    .block-item {
      border: 1px solid transparent;
      user-select: none;
      margin: 20px 0;
      box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
      background: #f7f8f9;
      display: flex;
      align-items: center;
      padding: 15px;
      border-radius: 12px;
      .inp {
        background-color: transparent;
        margin-left: 10px;
        .el-textarea__inner {
          background-color: transparent;
          user-select: none;
          border: 0;
        }
      }
    }
  }
}
.card {
  width: 100vw;
  height: 100vh;
  background-color: transparent;
  z-index: 2;
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  .card-body {
    min-width: 60px;
    padding: 4px;
    border-radius: 4px;
    background-color: #fff;
    box-shadow: 0px 3px 8px rgb(0 0 0 / 6%);
    position: fixed;
    z-index: 10;
    transition: all 0.5s;
    .card-li {
      padding: 8px 20px;
      cursor: pointer;
      &:hover {
        background-color: $hover-color;
      }
    }
  }
}
.el-textarea.is-disabled .el-textarea__inner {
  cursor: default;
}
</style>